﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;

namespace WorldOnFire
{
    public abstract class ABCMovingObject : ABCObject
    {
        #region Moving Object Data

        // Collision data
        private List<int> m_TunnelingIndexList;
        protected List<eObjectType> m_CollidableTypeList;

        // Physics data
        protected Vector3[] m_vPreviousPosition;
        protected Vector3[] m_vVelocity;
        protected Vector3[] m_vAcceleration;
        protected Vector3[] m_vAppliedForce;
        protected float m_fMass;

        #endregion Moving Object Data

        #region Accessors and Mutators

        public List<int> GetTunnelingIndexList()
        { return m_TunnelingIndexList; }

        public List<eObjectType> GetCollidableTypeList()
        { return m_CollidableTypeList; }

        public Vector3 GetProjectedPosition(int _nIndex, float _fTime)
        { return m_mTransform[_nIndex].Translation + m_vVelocity[_nIndex] * _fTime; }

        public Vector3 GetVelocity(int _nIndex)
        { return m_vVelocity[_nIndex]; }

        public float GetSpeed(int _nIndex)
        { return m_vVelocity[_nIndex].Length(); }

        public float GetMass()
        { return m_fMass; }

        public void ResetAcceleration(int _nIndex)
        { m_vAcceleration[_nIndex] = Vector3.Zero; }

        #endregion Accessors and Mutators

        #region Moving Object Functions

        public void ApplyForce(int _nIndex, Vector3 _vForce)
        { m_vAppliedForce[_nIndex] += _vForce; }

        public void ApplyCollisionCorrection(int _nIndex, Vector3 _vCorrection)
        { m_mTransform[_nIndex].Translation += _vCorrection; }

        public Vector3 CalculateProjectedPosition(int _nIndex, float _fTime)
        { return m_mTransform[_nIndex].Translation + (_fTime * m_vVelocity[_nIndex]); }

        #endregion Moving Object Functions

        #region Initialization

        public ABCMovingObject()
        {

        }

        public override void  Initialize(int _nObjectCount)
        {
            // Initialize the members
            m_TunnelingIndexList = new List<int>();
            m_CollidableTypeList = new List<eObjectType>();

            m_vPreviousPosition = new Vector3[_nObjectCount];
            m_vVelocity = new Vector3[_nObjectCount];
            m_vAcceleration = new Vector3[_nObjectCount];
            m_vAppliedForce = new Vector3[_nObjectCount];

            // Set the arrays to default values
            for (int iObject = 0; iObject < _nObjectCount; ++iObject)
            {
                m_vPreviousPosition[iObject] = Vector3.Zero;
                m_vVelocity[iObject] = Vector3.Zero;
                m_vAcceleration[iObject] = Vector3.Zero;
                m_vAppliedForce[iObject] = Vector3.Zero;

                // TODO: INITIALIZE MASS IN DERIVED CLASSES, YOU LAZY FUCK
                m_fMass = 1.0f;
            }

            // Initialize the base members
            base.Initialize(_nObjectCount);
        }

        public void InitializeMovingObject(int _nIndex)
        {
            // Zero out moving object data
            m_vPreviousPosition[_nIndex] = Vector3.Zero;
            m_vVelocity[_nIndex] = Vector3.Zero;
            m_vAcceleration[_nIndex] = Vector3.Zero;
            m_vAppliedForce[_nIndex] = Vector3.Zero;
        }

        #endregion Initialization

        #region Update

        public override void Update(float _fDeltaTime)
        {
            // Clear the tunneling index list
            m_TunnelingIndexList.Clear();

            // Update the movement data
            for (int iObject = 0; iObject < m_nObjectCount; ++iObject)
            {
                // If the object is inactive
                if (m_bObjectActive[iObject] == false)
                {
                    // Skip this turret
                    continue;
                }

                // Scale down acceleration every frame
                m_vAcceleration[iObject] *= (1.0f - _fDeltaTime);

                // Determine the acceleration (A = F / M)
                m_vAcceleration[iObject] += m_vAppliedForce[iObject] / m_fMass;

                // Reset the applied force
                m_vAppliedForce[iObject] = Vector3.Zero;

                // Add the acceration to the velocity (Vf = Vi + (A * t))
                m_vVelocity[iObject] += m_vAcceleration[iObject] * _fDeltaTime;

                // Save the current position;
                m_vPreviousPosition[iObject] = m_mTransform[iObject].Translation;

                // Add the velocity to the position (Pf = Pi + (V * t))
                Vector3 vMovement = m_vVelocity[iObject] * _fDeltaTime;
                m_mTransform[iObject].Translation += vMovement;

                // If the distance between the current and previous position exceeds the radius
                if (vMovement.LengthSquared() > m_fRadius[iObject] * m_fRadius[iObject])
                {
                    // Tunneling may have occured; add it to the tunneling index list
                    m_TunnelingIndexList.Add(iObject);
                }
            }

            base.Update(_fDeltaTime);
        }

        #endregion Update
    }
}
